/**OAuth2配置文件,主要用于配置Client和Token
 * ClientDetailsService：InMemoryClientDetailsService、JdbcClientDetailsService
 * authorizedGrantTypes：authorization_code、implicit、password、client_credentials、refresh_token
 * TokenStore：InMemoryTokenStore、JdbcTokenStore、RedisTokenStore、JwtTokenSTore、JwkTokenStore
 * TokenService：AuthorizationServerTokenServices（ 实现类：DefaultTokenServices）、ResourceServerTokenServices（实现类：RemoteTokenServices、DefaultTokenServices）
 */
package com.example.demoauthserver;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.types.RedisClientInfo;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.InMemoryClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.jwk.JwkTokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

@Configuration
@EnableAuthorizationServer
public class MyAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private DataSource dataSource;

    @Autowired
    RedisConnectionFactory redisConnectionFactory;

    @Autowired
    CustomAdditionalInformation customAdditionalInformation;

    private String SIGNING_KEY = "xotty";

     JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(SIGNING_KEY);
        return converter;
    }
   @Bean
    TokenStore myTokenStore(){
//      return new InMemoryTokenStore();                       //将 access_token 保存到 内存中，缺省方式
      return new JdbcTokenStore(dataSource);                 //将 access_token 保存到 数据库
//        return new RedisTokenStore(redisConnectionFactory);  //将 access_token 保存到 Redis
//        return new JwkTokenStore(xxx);                       //将 access_token 保存到 JSON Web Key
//        return new JwtTokenStore(jwtAccessTokenConverter());   //将 access_token 保存为 JSON Web Token
    }

    @Bean
    AuthorizationCodeServices authorizationCodeServices() {

//      return new InMemoryAuthorizationCodeServices();       //将 Authorization_Code 保存到 内存中，缺省方式
        return new JdbcAuthorizationCodeServices(dataSource);   //将 Authorization_Code 保存到数据库
    }

    @Bean
    ClientDetailsService myClientDetailsService() {
//        return new InMemoryClientDetailsService();
        return new JdbcClientDetailsService(dataSource);
    }

    @Bean
    AuthorizationServerTokenServices tokenServices() {
        DefaultTokenServices services = new DefaultTokenServices();
        services.setClientDetailsService(myClientDetailsService());
        services.setSupportRefreshToken(true);
        services.setTokenStore(myTokenStore());
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter(), customAdditionalInformation));
        services.setTokenEnhancer(tokenEnhancerChain);
        return services;
    }

    //配置认证客户端：client id/client secret
    //用clientDetailServiceBuilder构造client；除clients.inMemory外还可以用 clients.jdbc(dataSource)和clients.withClientDetails(clientDetailsService)
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        String finalSecret = "--------" + passwordEncoder.encode("mysecret")+"--------";
        System.out.println(finalSecret);
        System.out.println(passwordEncoder.encode("12345"));

//         clients.inMemory()
//                .withClient("client_1")
//                .secret(passwordEncoder.encode("mysecret"))
//                .scopes("webclient")
//                .authorizedGrantTypes("authorization_code","password")
//                .redirectUris("http://localhost:8780/result")
//                .and()
//                .withClient("client_2")
//                .authorizedGrantTypes("password", "refresh_token")
//                .scopes("mobileclient")
//                .authorities("oauth2")
//                .secret(passwordEncoder.encode("mysecret"));
////                .resourceIds("resource_1")     //可以访问的resourceId
////                .accessTokenValiditySeconds(5000)
////                .refreshTokenValiditySeconds(50000);

               clients.withClientDetails(myClientDetailsService());
    }



    //配置端点
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter(), customAdditionalInformation));

        endpoints
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService)
                .authorizationCodeServices(authorizationCodeServices())     //配置授权码的存储方式
                .tokenStore(myTokenStore());                                //配置令牌的存储方式
//                .accessTokenConverter(jwtAccessTokenConverter())          //使用JWT
//                .tokenEnhancer(tokenEnhancerChain)                        //使用令牌增强
//                .tokenServices(tokenServices())                           //定义所有和Token有关配置，取代tokenStore、tokenEnhancer等
    }

    //配置安全
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.checkTokenAccess("isAuthenticated()")     //允许资源服务调用校验接口
                   .allowFormAuthenticationForClients();      //允许表单提交访问 OAuth2 授权接口，否则要在请求头添加参数Authorization:basic clientId:clientSecret的base64加密
//             .tokenKeyAccess("permitAll()")            //允许所有用户访问获取 Token 接口：GET/POST /oauth/token_key，打开默认受保护的/oauth/token_key端点
//             .checkTokenAccess("isAuthenticated()")    //允许已验证用户访问checkToken 接口：POST  /oauth/check_token,打开默认受保护的/oauth/check_token端点
//             .passwordEncoder(new BCryptPasswordEncoder())     //用BCrypt加密client_secret
    }


    //获取token时接受get方式
    @Autowired
    private TokenEndpoint tokenEndpoint;
    @PostConstruct
    public void reconfigure() {
        Set<HttpMethod> allowedMethods =
                new HashSet<>(Arrays.asList(HttpMethod.GET, HttpMethod.POST));
        tokenEndpoint.setAllowedRequestMethods(allowedMethods);
    }
}
